#pragma once

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/service.h>

#include "EZRPC_Signal.h"
#include "EZRPC_Ctl.h"
#include "EZWebsocketClient.h"
#include "transport/EZNoTlsTransport.h"
#include "loop/EZTimer.h"


class EZRPC_Channel : public google::protobuf::RpcChannel
{
public:
	EZRPC_Channel();
	~EZRPC_Channel();

	const std::string& remote_address() const { return url_; }

	bool connect(const std::string& url);

	void close() { connecter_.close(); }

private:
	// Call the given method of the remote service.  The signature of this
	// procedure looks the same as Service::CallMethod(), but the requirements
	// are less strict in one important way:  the request and response objects
	// need not be of any specific class as long as their descriptors are
	// method->input_type() and method->output_type().
	virtual void CallMethod(const google::protobuf::MethodDescriptor* method,
		google::protobuf::RpcController* controller,
		const google::protobuf::Message* request,
		google::protobuf::Message* response,
		google::protobuf::Closure* done);
private:
	void on_ws_message(const EZWebsocket::EZWebsocketConPtr& stream,
        const EZWebsocket::EZWebsocketMsg& msg);
private:
	struct msg_stub
	{
		msg_stub() : controller(nullptr),response(nullptr), done(nullptr){}

		EZRPC_SignalPtr  sig;
		EZTcp::EZTimerPtr async_timer;
		EZRPC_Ctl* controller;
		google::protobuf::Message* response;
		google::protobuf::Closure* done;
	};
private:
	void async_timeout(int rid,const std::error_code& e);

    void async_reconnect(EZTcp::EZTimerPtr tmr);
private:
	bool												connected_;
	std::shared_ptr<std::thread>						io_thread_;
	EZWebsocket::EZWebsocketClient<EZNoTlsTransport>	connecter_;
	std::string			url_;
	int					req_id_;
	std::mutex									mutex_;
	std::map<int, std::shared_ptr<msg_stub>>	stub;
};

